// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/output/context_cache_controller.h"

#include "base/memory/ptr_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "cc/test/test_context_provider.h"
#include "cc/test/test_context_support.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {
    using ::testing::Mock;
    using ::testing::StrictMock;

    class MockContextSupport : public TestContextSupport {
    public:
        MockContextSupport() { }
        MOCK_METHOD1(SetAggressivelyFreeResources,
            void(bool aggressively_free_resources));
    };

    TEST(ContextCacheControllerTest, ScopedVisibilityBasic)
    {
        StrictMock<MockContextSupport> context_support;
        auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
        ContextCacheController cache_controller(&context_support, task_runner);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        std::unique_ptr<ContextCacheController::ScopedVisibility> visibility = cache_controller.ClientBecameVisible();
        Mock::VerifyAndClearExpectations(&context_support);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        cache_controller.ClientBecameNotVisible(std::move(visibility));
    }

    TEST(ContextCacheControllerTest, ScopedVisibilityMulti)
    {
        StrictMock<MockContextSupport> context_support;
        auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
        ContextCacheController cache_controller(&context_support, task_runner);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        auto visibility_1 = cache_controller.ClientBecameVisible();
        Mock::VerifyAndClearExpectations(&context_support);
        auto visibility_2 = cache_controller.ClientBecameVisible();

        cache_controller.ClientBecameNotVisible(std::move(visibility_1));
        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        cache_controller.ClientBecameNotVisible(std::move(visibility_2));
    }

    TEST(ContextCacheControllerTest, ScopedBusyWhileVisible)
    {
        StrictMock<MockContextSupport> context_support;
        auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
        ContextCacheController cache_controller(&context_support, task_runner);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        auto visibility = cache_controller.ClientBecameVisible();
        Mock::VerifyAndClearExpectations(&context_support);

        // Now that we're visible, ensure that going idle triggers a delayed cleanup.
        auto busy = cache_controller.ClientBecameBusy();
        cache_controller.ClientBecameNotBusy(std::move(busy));

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
        Mock::VerifyAndClearExpectations(&context_support);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        cache_controller.ClientBecameNotVisible(std::move(visibility));
    }

    TEST(ContextCacheControllerTest, ScopedBusyWhileNotVisible)
    {
        StrictMock<MockContextSupport> context_support;
        auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
        ContextCacheController cache_controller(&context_support, task_runner);

        auto busy = cache_controller.ClientBecameBusy();

        // We are not visible, so becoming busy should not trigger an idle callback.
        cache_controller.ClientBecameNotBusy(std::move(busy));
        task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
    }

    TEST(ContextCacheControllerTest, ScopedBusyMulitpleWhileVisible)
    {
        StrictMock<MockContextSupport> context_support;
        auto task_runner = make_scoped_refptr(new base::TestMockTimeTaskRunner);
        ContextCacheController cache_controller(&context_support, task_runner);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        auto visible = cache_controller.ClientBecameVisible();
        Mock::VerifyAndClearExpectations(&context_support);

        auto busy_1 = cache_controller.ClientBecameBusy();
        cache_controller.ClientBecameNotBusy(std::move(busy_1));
        auto busy_2 = cache_controller.ClientBecameBusy();
        cache_controller.ClientBecameNotBusy(std::move(busy_2));

        // When we fast forward, only one cleanup should happen.
        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
        task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
        Mock::VerifyAndClearExpectations(&context_support);

        EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
        cache_controller.ClientBecameNotVisible(std::move(visible));
    }

} // namespace
} // namespace cc
